package org.datascooter.db.oracle;

import java.text.MessageFormat;

import org.datascooter.bundle.DSMapper;
import org.datascooter.bundle.EntityBundle;
import org.datascooter.bundle.attribute.PersistAttribute;
import org.datascooter.exception.EntityNotMappedException;
import org.datascooter.impl.DataSnip;
import org.datascooter.impl.Snip;
import org.datascooter.impl.SnipBuilder;
import org.datascooter.inface.ISnipBuilder;
import org.datascooter.meta.Column;
import org.datascooter.utils.DBType;
import org.datascooter.utils.DSSettings;
import org.datascooter.utils.LangUtils;
import org.datascooter.utils.SnipUtils;
import org.datascooter.utils.policy.SnipType;

public class OracleSnipBuilder extends SnipBuilder {

	protected String VARCHAR2 = "VARCHAR2";
	protected String DATE = "DATE";

	public OracleSnipBuilder() {
		super("oracle");
	}

	@Override
	public ISnipBuilder copy() {
		return new OracleSnipBuilder();
	}

	@Override
	protected void buildKeysClause(DataSnip dataSnip) throws EntityNotMappedException {
		if (dataSnip.bundle.id != null) {
			commaTokens.append(MessageFormat.format(
				"ALTER TABLE {0} ADD CONSTRAINT PK_{1}_{2} PRIMARY KEY ({3}) USING INDEX",
				getCaseSensitiveTableName(dataSnip.bundle.getTableName()),
				LangUtils.abbreviate(dataSnip.bundle.table, 6, SnipUtils.UNDERLINE, 2),
				LangUtils.getRandomIndexString(4),
				dataSnip.bundle.id.getColumnName()));
		}
	}

	@Override
	public Snip buildUpdateColumnSnip(String entity, PersistAttribute attribute) throws EntityNotMappedException, SecurityException, NoSuchMethodException {
		EntityBundle bundle = DSMapper.getBundle(entity);
		return new Snip(null, SnipType.EXECUTE, MessageFormat.format("ALTER TABLE {0} MODIFY {1}", bundle.getTableName(), buildColumnClause(attribute)), null, null, null, -1, null);
	}

	@SuppressWarnings("nls")
	@Override
	public String buildColumnClause(PersistAttribute attribute) {
		String typeStr = getDbTypeString(attribute.getType());
		String size = buildDataLength(attribute.getScale(), attribute.getPrecision());
		DBType type = attribute.getType();
		switch (type) {
			case BIGDECIMAL:
				typeStr = SnipUtils.NUMBER;
				size = "(22,2)";
				break;
			case LONG:
				typeStr = SnipUtils.NUMBER;
				size = "(22)";
				break;
			case BOOLEAN:
				typeStr = SnipUtils.NUMBER;
				size = "(1)";
				break;
			case INT:
				typeStr = SnipUtils.NUMBER;
				size = "(14)";
				break;
			case STRING:
				typeStr = VARCHAR2;
				if (size.length() == 0) {
					size = "(" + DSSettings.getDefaultStringLength() + ")";
				}
				break;
			case CHAR:
				typeStr = SnipUtils.CHAR;
				if (size.length() == 0) {
					size = "(" + DSSettings.getDefaultStringLength() + ")";
				}
				break;
			case BLOB:
				typeStr = SnipUtils.BLOB;
				break;
			case CLOB:
				typeStr = SnipUtils.CLOB;
				break;
			case TIMESTAMP:
				typeStr = DATE;
				break;
		}
		return SnipUtils.SP + attribute.getColumnName() + SnipUtils.SP + typeStr + SnipUtils.SP + size + (attribute.isNullable() ? SnipUtils.NN : SnipUtils.NOT_NULL);
	}

	@Override
	public boolean equalType(PersistAttribute attribute, Column column) {
		boolean result = false;
		boolean scaled = true;
		if (attribute.getType() == null) {
			return true;
		}
		switch (attribute.getType()) {
			case BIGDECIMAL:
				result = SnipUtils.NUMBER.equalsIgnoreCase(column.dataTypeName);
				break;
			case LONG:
				result = SnipUtils.NUMBER.equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case BOOLEAN:
				result = SnipUtils.NUMBER.equalsIgnoreCase(column.dataTypeName);
				break;
			case INT:
				result = SnipUtils.NUMBER.equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
			case STRING:
				result = VARCHAR2.equalsIgnoreCase(column.dataTypeName);
				break;
			case CHAR:
				result = SnipUtils.CHAR.equalsIgnoreCase(column.dataTypeName);
				break;
			case BLOB:
				result = SnipUtils.BLOB.equalsIgnoreCase(column.dataTypeName);
				break;
			case CLOB:
				result = SnipUtils.CLOB.equalsIgnoreCase(column.dataTypeName);
				break;
			case TIMESTAMP:
				result = DATE.equalsIgnoreCase(column.dataTypeName) || "TIMESTAMP".equalsIgnoreCase(column.dataTypeName);
				scaled = false;
				break;
		}
		result = checkScale(attribute, column, result, scaled);
		return result;
	}

	@Override
	public void buildPrimaryIndex(DataSnip dataSnip) {
	}

	@SuppressWarnings("nls")
	@Override
	public String managePage(DataSnip dataSnip, String sqlClause) {
		if (dataSnip.fetchSize == 0) {
			return sqlClause;
		}
		return "select * from ( select p.*, rownum rnum from (" + sqlClause + ") p where rownum < " + (dataSnip.fromRow + 1 + dataSnip.fetchSize) + " ) where rnum >  " + dataSnip.fromRow;
	}
}
